Fix remaining #470 console flash: hub server spawn, not just browser-open - #475
Fix remaining #470 console flash: hub server spawn, not just browser-open#475richard-devbot wants to merge 2 commits into
Conversation
Slack notifications have never been deliverable. Both payload builders in
src/notifications/index.js emitted a block of `{ type: 'fields', fields }`,
but Block Kit has no `fields` block type — fields belong on a `section`
block. Slack rejects the entire attachment with:
400 invalid_attachments
Verified against a live Incoming Webhook: failing before this change,
delivering successfully after it.
The bogus type had propagated to every consumer, so each one is updated to
read fields off a section block while still tolerating the old shape, which
keeps any in-flight or persisted payloads converting correctly:
- channels/teams.js - facts extraction
- channels/discord.js - embed fields
- channels/text.js - plain-text flattening (covered by the existing
"slackPayloadToText renders blocks as readable
plain text" test, which caught this consumer)
Teams and Discord converters produce identical output to before the change.
tests/notifications-channels.test.js, harness-notifications.test.js and
notify-hook.test.js pass 21/21.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…open #471 fixed the cmd.exe browser-open spawn (`start <url>` via shell:true) at four call sites, but each of those files makes a SECOND spawn that #471 missed: launching the Business Hub server itself by spawning node.exe directly with `detached: true` and no `windowsHide`. node.exe is a console-subsystem executable. Spawning one detached without windowsHide flashes a visible console window on win32 independent of the shell:true/cmd.exe case #471 addressed — so the flash kept happening on every Claude Code session start (SessionStart hook -> autoLaunchBusinessHub) and every Pi session start, exactly where users would notice it most. Fixed the two call sites: - src/hooks/auto-launch.js (Claude Code SessionStart hook) - src/integrations/pi/rstack-sdlc.ts (Pi session start) autoLaunchBusinessHub now accepts an injectable `spawnImpl` (mirroring the existing openBrowser/startContainerRuntime convention) so the windowsHide behavior on the server-launch spawn is unit-testable without spawning a real background process. Added a regression test to tests/windows-console-flash-470.test.js alongside the existing #470 coverage. Full suite: 1559 pass / 51 fail, same 51 as an unmodified tree — all spawn ENOENT / temp-dir EBUSY, the documented pre-existing Windows-sandbox limitations, none touching the changed files. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Strix Security ReviewNo security issues found. Updated for Reviewed by Strix |
📝 WalkthroughWalkthroughBusiness Hub spawn paths now hide detached Windows consoles and support injected spawning for tests. Slack notification formatting now emits standard section field blocks, while converters accept both section and legacy field payloads. ChangesBusiness Hub console launch
Slack field payload handling
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
PR Summary by QodoFix Win console flash on hub spawn; correct Slack Block Kit fields
AI Description
Diagram
High-Level Assessment
Files changed (7)
|
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tests/windows-console-flash-470.test.js`:
- Around line 107-114: Make the autoLaunchBusinessHub spawn-branch test
deterministic by stubbing or injecting the isPortOpen check so the configured
port is guaranteed closed, and explicitly set RSTACK_NO_BUSINESS_HUB to enable
launching. Capture and restore the original RSTACK_NO_BUSINESS_HUB value
alongside the existing environment variables, preserving cleanup in all paths.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: a68eea8f-56b8-4342-ac75-d644031ee94a
📒 Files selected for processing (7)
src/hooks/auto-launch.jssrc/integrations/pi/rstack-sdlc.tssrc/notifications/channels/discord.jssrc/notifications/channels/teams.jssrc/notifications/channels/text.jssrc/notifications/index.jstests/windows-console-flash-470.test.js
| // A high, effectively-never-bound port so isPortOpen() resolves false and | ||
| // the function takes the "spawn a fresh instance" branch under test. | ||
| const originalPort = process.env.RSTACK_BUSINESS_PORT; | ||
| const originalNoBrowser = process.env.RSTACK_NO_BROWSER; | ||
| process.env.RSTACK_BUSINESS_PORT = '58471'; | ||
| process.env.RSTACK_NO_BROWSER = '1'; | ||
| try { | ||
| await autoLaunchBusinessHub('/tmp/project', { spawnImpl: recordingSpawn(calls) }); |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
Make the spawn branch deterministic.
isPortOpen() checks the real TCP port 58471, so another process can make the test skip the spawn and fail at calls.length === 1. The test also does not override an inherited RSTACK_NO_BUSINESS_HUB=1, which can return before spawning. Inject/stub the port check (or otherwise guarantee a closed port) and explicitly set/restore RSTACK_NO_BUSINESS_HUB.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@tests/windows-console-flash-470.test.js` around lines 107 - 114, Make the
autoLaunchBusinessHub spawn-branch test deterministic by stubbing or injecting
the isPortOpen check so the configured port is guaranteed closed, and explicitly
set RSTACK_NO_BUSINESS_HUB to enable launching. Capture and restore the original
RSTACK_NO_BUSINESS_HUB value alongside the existing environment variables,
preserving cleanup in all paths.
Code Review by Qodo
Context used✅ Compliance rules (platform):
300 rules✅ Skills:
|
| // Fields ride on a section block; 'fields' is tolerated for older payloads. | ||
| if ((block.type === 'section' || block.type === 'fields') && block.fields) { | ||
| for (const f of block.fields) { | ||
| const raw = f.text || ''; | ||
| const parts = raw.split('\n'); |
There was a problem hiding this comment.
1. Duplicated slack fields parsing 📜 Skill insight ⚙ Maintainability
The PR repeats the same Slack Block Kit fields-handling logic (and explanatory comment) across multiple notification channel converters, creating a DRY violation and increasing maintenance risk if the parsing behavior changes again.
Agent Prompt
## Issue description
The Slack `fields` parsing logic is duplicated across multiple notification channel converters, violating DRY and making future schema tweaks easy to miss in one of the implementations.
## Issue Context
The same conditional logic and loop appear in `discord.js`, `teams.js`, and `text.js` to support both the correct Block Kit shape (`section` with `fields`) and a legacy tolerated shape (`type: 'fields'`).
## Fix Focus Areas
- src/notifications/channels/discord.js[24-33]
- src/notifications/channels/teams.js[27-40]
- src/notifications/channels/text.js[26-34]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| * The original #470/#471 fix covered the browser-open spawn (cmd.exe /c | ||
| * start) at each call site but missed the OTHER spawn each of those files | ||
| * makes: launching the Business Hub SERVER itself by spawning node.exe | ||
| * directly with detached: true. node.exe is a console-subsystem executable, | ||
| * so that spawn flashes a console too, independent of the shell:true case — | ||
| * it kept flashing after #471 merged. Covered below for both the Claude | ||
| * Code hook (auto-launch.js) and the Pi integration (rstack-sdlc.ts). | ||
| * |
There was a problem hiding this comment.
2. Regression test lacks attribution metadata 📜 Skill insight ⚙ Maintainability
The updated regression test comment block references #470/#471 but does not include the required found-date and QA report path metadata. This reduces traceability for why/when the regression coverage was added and where the original report lives.
Agent Prompt
## Issue description
The regression test header comment is missing required attribution metadata (date found and QA report path).
## Issue Context
Compliance requires regression tests to include: issue ID, what broke, date found, and QA report path.
## Fix Focus Areas
- tests/windows-console-flash-470.test.js[1-18]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| const originalPort = process.env.RSTACK_BUSINESS_PORT; | ||
| const originalNoBrowser = process.env.RSTACK_NO_BROWSER; | ||
| process.env.RSTACK_BUSINESS_PORT = '58471'; | ||
| process.env.RSTACK_NO_BROWSER = '1'; |
There was a problem hiding this comment.
4. 58471 hardcoded test port 📜 Skill insight ⚙ Maintainability
The test hardcodes the port value 58471 instead of using a named constant. This reduces readability and makes future updates more error-prone.
Agent Prompt
## Issue description
A literal port number is used directly in the test (`'58471'`), which violates the magic-number rule.
## Issue Context
This port is intended to be an effectively-unused port to force `isPortOpen()` to return false.
## Fix Focus Areas
- tests/windows-console-flash-470.test.js[109-112]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| // A high, effectively-never-bound port so isPortOpen() resolves false and | ||
| // the function takes the "spawn a fresh instance" branch under test. | ||
| const originalPort = process.env.RSTACK_BUSINESS_PORT; | ||
| const originalNoBrowser = process.env.RSTACK_NO_BROWSER; | ||
| process.env.RSTACK_BUSINESS_PORT = '58471'; | ||
| process.env.RSTACK_NO_BROWSER = '1'; | ||
| try { | ||
| await autoLaunchBusinessHub('/tmp/project', { spawnImpl: recordingSpawn(calls) }); | ||
| } finally { | ||
| if (originalPort === undefined) delete process.env.RSTACK_BUSINESS_PORT; else process.env.RSTACK_BUSINESS_PORT = originalPort; | ||
| if (originalNoBrowser === undefined) delete process.env.RSTACK_NO_BROWSER; else process.env.RSTACK_NO_BROWSER = originalNoBrowser; | ||
| } | ||
| assert.equal(calls.length, 1); | ||
| assert.equal(calls[0].cmd, process.execPath); | ||
| assert.equal(calls[0].options.detached, true); | ||
| assert.equal(calls[0].options.windowsHide, true); | ||
| }); |
There was a problem hiding this comment.
5. Flaky port-based test 🐞 Bug ☼ Reliability
The new autoLaunchBusinessHub regression test assumes port 58471 is unused so isPortOpen() returns false; if any process is listening on 127.0.0.1:58471, autoLaunchBusinessHub() will early-return and never call the injected spawnImpl, causing a nondeterministic test failure.
Agent Prompt
### Issue description
`tests/windows-console-flash-470.test.js` hard-codes `RSTACK_BUSINESS_PORT=58471` to force `autoLaunchBusinessHub()` down the spawn path. Because `autoLaunchBusinessHub()` performs a real TCP probe (`isPortOpen`) before spawning, a listener on that port will flip the branch and make the test fail intermittently.
### Issue Context
This test is intended to validate `windowsHide: true` on the Business Hub *server-launch* spawn. It should be deterministic and not depend on the machine’s ambient port usage.
### Fix Focus Areas
- src/hooks/auto-launch.js[10-35]
- tests/windows-console-flash-470.test.js[105-123]
### Suggested fix
Add a second injectable dependency to `autoLaunchBusinessHub`, e.g. `opts.isPortOpenImpl ?? isPortOpen`, and use that instead of calling `isPortOpen` directly. In the test, pass `isPortOpenImpl: async () => false` so the spawn path is guaranteed without relying on any specific port.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
Problem
The console-window flash from #470 is still happening — #471 fixed the wrong half of it.
Every affected file (
auto-launch.js,rstack-sdlc.ts,sandbox.js,dashboard/server.js) makes two spawns:spawn(cmd, [url], { shell: true })→ opens the browser viacmd.exe /c start— this one gotwindowsHidein Fix Windows console-window flash on browser/runtime auto-launch #471spawn(process.execPath, [binPath, ...], { detached: true })→ launches the Business Hub server itself — this one didn'tprocess.execPathisnode.exe, a console-subsystem executable. Spawning itdetached: truewithoutwindowsHideflashes a console window on win32 regardless ofshell— a separate cause from the one #471 fixed. It fires on the path users hit most: Claude CodeSessionStart→autoLaunchBusinessHub→ this spawn, and the equivalent Pi session-start path.Fix
Added
windowsHide: trueto the two missed call sites:src/hooks/auto-launch.js— Claude Code hub launchsrc/integrations/pi/rstack-sdlc.ts— Pi hub launchautoLaunchBusinessHubnow accepts an injectablespawnImpl(same convention asopenBrowser/startContainerRuntime) so this is unit-testable without spawning a real process.Verification
tests/windows-console-flash-470.test.js, alongside the existing Windows: browser/runtime auto-launch briefly flashes a visible console window #470 coverage — assertswindowsHide: trueon the server-launch spawn.tests/windows-console-flash-470.test.js: 7/7 pass.spawn npx ENOENT, Windows temp-dirEBUSY), the documented pre-existing sandbox limitations. None touch the changed files.🤖 Generated with Claude Code
Summary by CodeRabbit